package com.wlb.address.split.dict.defualt;

import static org.junit.Assert.assertEquals;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URLDecoder;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.wlb.address.split.domain.GeoTerm;

public class DArrayTrie {
	// 内码和序码之间的对应关系，注意从unsigned short 到int的转换
	// 在这个应用中，内码和序码相同，都是字符在内存中的编码，比如汉字编码为16位，其他字符可能8位
	// 最后在应用时候都转换为int，注意都是正整数，注意使用移位操作来做转换
	private int[] charMapArray; // 内码-->序码映射表
	private int[] codeMapArray; // 序码-->内码映射表

	// 版本号
	private String version;

	private int handleSize; // handle数组的长度
	private int wordSize;
	private int nDArraySize; // 双数组的长度

	private DATrieNode[] daTrieNodeArray; // 保存双数组Trie数据的地方
	private WordData[] wordDataArray; // 保存对应词性词频词条ID的地方，daTrieNode的handle>>4来对应

	private boolean bInit; // 是否初始化
	//不能使用硬编码，以后使用spring注入
	private String filePath = "D:\\addressSplit\\result\\charmap.txt";
	private String fileLoad;
	Map<String,Integer> map;

	public DArrayTrie() {
		handleSize = 0;
		nDArraySize = 0;
		wordSize = 0;

		version = null;
		charMapArray = null;
		codeMapArray = null;
		wordDataArray = null;
		daTrieNodeArray = null;
		bInit = true;
		map = charMapUtil.makeMap(filePath);
	}
	
	public DArrayTrie(String fileLoad){
		handleSize = 0;
		nDArraySize = 0;
		wordSize = 0;
		
		this.fileLoad = fileLoad;
		version = null;
		charMapArray = null;
		codeMapArray = null;
		wordDataArray = null;
		daTrieNodeArray = null;
		bInit = true;
		map = charMapUtil.makeMap(filePath);
	}

	public int[] getCharMapArray() {
		return charMapArray;
	}

	public void setCharMapArray(int[] charMap) {
		this.charMapArray = charMap;
	}

	public int[] getCodeMapArray() {
		return codeMapArray;
	}

	public void setCodeMapArray(int[] codeMap) {
		this.codeMapArray = codeMap;
	}

	public String getVersion() {
		return version;
	}

	public void setVersion(String version) {
		this.version = version;
	}

	public int getHandleSize() {
		return handleSize;
	}

	public void setHandleSize(int handleSize) {
		this.handleSize = handleSize;
	}

	public int getWordSize() {
		return wordSize;
	}

	public void setWordSize(int wordSize) {
		this.wordSize = wordSize;
	}

	public int getnDArraySize() {
		return nDArraySize;
	}

	public void setnDArraySize(int nDArraySize) {
		this.nDArraySize = nDArraySize;
	}

	public DATrieNode[] getDaTrieNodeArray() {
		return daTrieNodeArray;
	}

	public void setDaTrieNodeArray(DATrieNode[] daTrieNode) {
		this.daTrieNodeArray = daTrieNode;
	}

	public WordData[] getWordDataArray() {
		return wordDataArray;
	}

	public void setWordDataArray(WordData[] wordData) {
		this.wordDataArray = wordData;
	}

	public boolean isbInit() {
		return bInit;
	}

	public void setbInit(boolean bInit) {
		this.bInit = bInit;
	}

	public boolean load(){
		boolean b = false;
		try{
			b = load(fileLoad);
		}catch(IOException e){
			e.printStackTrace();
		}
		return b;
	}
	public boolean load(String fileName) throws IOException {
		// 打开字典文件
		File file = new File(fileName);
		if (!file.exists()) {
			throw new FileNotFoundException(fileName);
		}

		// ByteArrayOutputStream bos = new
		// ByteArrayOutputStream((int)file.length());
		BufferedInputStream in = null;
		try {
			in = new BufferedInputStream(new FileInputStream(file));
			// 开始处理自动文件
			// 一、版本号，读取version_size（64）个byte
			byte[] buffer = new byte[64];
			int len = in.read(buffer, 0, commData.VERSION_SIZE);
			if (len == -1) {
				return false;
			}

			version = new String(buffer);
			// 二、内码对应序码，在charMapArray、codeMapArray中保存
			if (charMapArray == null) {
				charMapArray = new int[commData.CHARSET_SIZE];
			}
			if (codeMapArray == null) {
				codeMapArray = new int[commData.CHARSET_SIZE];
			}
			// 因为对应的是unsigned short---2个字节
			buffer = new byte[commData.CHARSET_SIZE * 2];
			len = in.read(buffer, 0, commData.CHARSET_SIZE * 2);
			// 每个buffer中每次只取两个byte来翻译成int
			for (int i = 0; i < commData.CHARSET_SIZE * 2; i = i + 2) {
				int num1 = buffer[i + 1];
				int num2 = buffer[i];
				// int num = ((num1 & 0xff)<<8) | (num2 & 0xff);
				int num = byteToIntUtil.byteToInt(buffer, i, 2);
				// System.out.println(num);
				int index = i / 2;
				charMapArray[index] = num;
				codeMapArray[charMapArray[index]] = index;
			}
			// 读取handleSize，读取WordData(pos,freq,termid)的值，赋值给wordData
			buffer = new byte[4];
			len = in.read(buffer, 0, 4);
			handleSize = byteToIntUtil.byteToInt(buffer, 0, 4);
			wordSize = handleSize;

			buffer = new byte[handleSize * 8];
			len = in.read(buffer, 0, handleSize * 8);

			// 给wordDataArray赋值
			wordDataArray = new WordData[handleSize]; // 分配内存

			for (int i = 0; i < handleSize; i++) {
				int pos = byteToIntUtil.byteToInt(buffer, i * 8, 2);
				int freq = byteToIntUtil.byteToInt(buffer, i * 8 + 2, 2);
				int termID = byteToIntUtil.byteToInt(buffer, i * 8 + 4, 4);

				wordDataArray[i] = new WordData(pos, freq, termID);
				//System.out.println("pos:"+pos+"  freq:"+freq+"  termID:"+termID);
			}
			// 3-读取nDArraySize，读取daTrieNodeArray(base,check,handle)的值
			buffer = new byte[4];
			len = in.read(buffer, 0, 4);
			nDArraySize = byteToIntUtil.byteToInt(buffer, 0, 4);
			// System.out.println(nDArraySize);
			daTrieNodeArray = new DATrieNode[nDArraySize];
			buffer = new byte[nDArraySize * 12];
			len = in.read(buffer, 0, nDArraySize * 12);
			for (int i = 0; i < nDArraySize; i++) {
				int base = byteToIntUtil.byteToInt(buffer, i * 12, 4);
				int check = byteToIntUtil.byteToInt(buffer, i * 12 + 4, 4);
				int handle = byteToIntUtil.byteToInt(buffer, i * 12 + 8, 4);

				daTrieNodeArray[i] = new DATrieNode(base, check, handle);
			}
			// 4-bInit的设置
			bInit = true;
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			in.close();
		}
		return false;
	}

	//在这个函数里面，只会取到一个词
	public GeoTerm lookup(String splitWord) {
		// 初始状态为1
		int base, check, state = 1;
		int index = 0;
		boolean flag_max_length = false;
		String rWord = "";
		String tWord = "";
		String rInsert = "";
		int cid = 0;
		if (splitWord.isEmpty() || splitWord.trim().equals("")) {
			return null;
		}
		try {
			String split = new String(splitWord.getBytes(),"GBK");
			Charset charset = Charset.forName("GBK");
			ByteBuffer byteBuffer = charset.encode(splitWord);
			CharBuffer charBuffer = charset.decode(byteBuffer);
			
			int length = splitWord.length();
			base = daTrieNodeArray[state].getBase();
			
			// 开始轮询处理splitWord中的每一个单词
			for (int i = 0; i < length;i++) {
				char c = charBuffer.get(i);
				if (byteToIntUtil.isChinese(c)) { // 是汉字，关注字节编码是否有错
					rInsert = String.valueOf(c);
					rWord = rWord + rInsert;
					index = map.get(String.valueOf(c));
				} else { // 不是汉字，是单字节编码
					rInsert = String.valueOf(c);
					rWord = rWord + rInsert;
					index = (int)c;
				}
				
				cid = charMapArray[index]; // 内码转序码,使用专门为java使用的转换map文件
				if (cid == 0 || base + cid >= nDArraySize) {
					break;
				}
				check = daTrieNodeArray[base + cid].getCheck();
				if (check != state) {  //在这里判断最长匹配，是否后面还有可以匹配的词，没有跳出
					break;
				}
				state = base + cid;
				base = daTrieNodeArray[state].getBase();
				if ((base & 0x80000000) != 0) { // 可以在这里切词,在这里给terms赋值
					//在这里还有一个问题，如何做最长匹配
					base &= 0x7FFFFFFF;
					flag_max_length = true;
					tWord = rWord;
				}
			}
			
			//在这里给返回的GeoTerm赋值
			if(flag_max_length == true){
				int handle = daTrieNodeArray[state].getHandle()>>4;
				int pos = wordDataArray[handle].getnPOS();
				int len = tWord.length();
				String prop = String.valueOf(pos);
				GeoTerm term= new GeoTerm(tWord, prop,len);
				return term;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		return null;
	}

	public  int splitWordIntoTerm(String splitWord,List<GeoTerm> terms){
		String intern = splitWord;
		GeoTerm gt = null;
		int num = 0;
		for(;;){
			gt = lookup(intern);
			if(gt != null){
				terms.add(gt);
				int strLen = gt.getLen();
				if(strLen == intern.length()){
					break;
				}
				intern = intern.substring(strLen);
				num++;
			}else{
				break;
			}
		}
		return num;
	}
	
	
	
	public static void main(String[] args) {
		  if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) {
		      System.out.println("BIG_ENDIAN");
		    } else {
		      System.out.println("LITTLE_ENDIAN");
		    }
		DArrayTrie datrie = new DArrayTrie();
		String addr = "上海市浦东新区张江高科蔡伦路1433号1号楼1002室";
		GeoTerm term=null;
		try {
			//需要加载这个dict.dat文件和charmap.txt
			boolean b = datrie.load("D:\\addressSplit\\newdict\\dict.dat");
		    term = datrie.lookup(addr);
			if(term != null){
				System.out.println(term.getWord()+",it's length is:"+term.getLen());
			}else{
				System.out.println("Cannot split the string");
			}
			List<GeoTerm> terms = new ArrayList<GeoTerm>();
			int result = datrie.splitWordIntoTerm(addr,terms);
			
			System.out.println(result);
			for(int i=0;i<result;i++){
				System.out.println(terms.get(i).getWord()+",it's length is:"+terms.get(i).getLen()+",it's prop is:"+terms.get(i).getProp());
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}
